package com.example.yijian.edit.clipEdit.adjust;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;

import com.example.yijian.R;
import com.example.yijian.base.NvsConstants;
import com.example.yijian.edit.clipEdit.view.CutRectLayout;
import com.example.yijian.edit.data.BackupData;
import com.example.yijian.utils.AppManager;
import com.example.yijian.utils.Constants;
import com.example.yijian.utils.FormatUtils;
import com.example.yijian.utils.LogUtils;
import com.example.yijian.utils.StoryboardUtil;
import com.example.yijian.utils.TimelineUtil;
import com.example.yijian.utils.asset.NvAsset;
import com.example.yijian.utils.datainfo.ClipInfo;
import com.example.yijian.utils.datainfo.CutData;
import com.example.yijian.utils.datainfo.TimelineData;
import com.meicam.sdk.NvsAVFileInfo;
import com.meicam.sdk.NvsLiveWindow;
import com.meicam.sdk.NvsLiveWindowExt;
import com.meicam.sdk.NvsPanAndScan;
import com.meicam.sdk.NvsSize;
import com.meicam.sdk.NvsStreamingContext;
import com.meicam.sdk.NvsTimeline;
import com.meicam.sdk.NvsVideoClip;
import com.meicam.sdk.NvsVideoFx;
import com.meicam.sdk.NvsVideoResolution;
import com.meicam.sdk.NvsVideoTrack;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static com.example.yijian.utils.StoryboardUtil.STORYBOARD_KEY_SCALE_X;
import static com.example.yijian.utils.StoryboardUtil.STORYBOARD_KEY_SCALE_Y;
import static com.example.yijian.utils.StoryboardUtil.STORYBOARD_KEY_ROTATION_Z;
import static com.example.yijian.utils.StoryboardUtil.STORYBOARD_KEY_TRANS_X;
import static com.example.yijian.utils.StoryboardUtil.STORYBOARD_KEY_TRANS_Y;

public class AdjustCropActivity extends AppCompatActivity {
    private static final String TAG = "AdjustCropActivity";
    private static final String PARAM_MAX_DURATION = "max_duration";
    private static final int MESSAGE_RESET_PLAY_BACK_STATE = 100;
    private NvsLiveWindowExt mLiveWindow;
    private NvsTimeline mTimeline;
    private NvsVideoFx mVideoFx;
    private NvsVideoClip mVideoClip;
    private ArrayList<ClipInfo> mClipArrayList;
    private CutData mCutData;
    private int mRatio = NvsConstants.AspectRatio.AspectRatio_NoFitRatio;
    private float mRatioValue = -1F;
    private int mOriginalTimelineWidth;
    private int mOriginalTimelineHeight;
    private int mCurClipIndex = 0;
    private int mScaleX = 1;
    private int mScaleY = 1;
    private int mRotateAngle = 0;
    private NvsStreamingContext mStreamingContext = NvsStreamingContext.getInstance();
    private RelativeLayout mPlayerLayout;
    private long mStartTime;
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == MESSAGE_RESET_PLAY_BACK_STATE) {
                playVideo(mStartTime, mStartTime + getDuration());
            }
            return false;
        }
    });
    private long mMaxDuration = 0L;
    //    private OnPlayProgressChangeListener mOnPlayProgessChangeListener;
//    private OnFragmentLoadFinisedListener mFragmentLoadFinisedListener;
//    private VideoFragmentListener mVideoFragmentCallBack;
//    private OnCutRectChangedListener mOnCutRectChangedListener;
    private CutRectLayout mCutView;
    private float mMinLiveWindowScale = 1.0F;
    private Point mOriginalSize;
    private View mPlayButton;
    private TextView mCurrentPlaytimeView;
    private SeekBar mSeekBar;
    private ImageView mPlayButtonImage;
    private CuttingMenuView mCuttingMenuView;
    /**
     * 震动器
     */
    private Vibrator mVibrator;
    boolean hasVibrator = false;
    private FloatPoint mCenterPoint = new FloatPoint();
    private TextView mTotalDurationView;
    /**
     * 当前编辑的临时缩放值。用于旋转时，能恢复到这个值
     * The temporary scaling value of the current edit.
     * When used for rotation, it can be restored to this value.
     */
    private float mTempScale = 1.0F;

    private Map<String, Float> mTransformData = new HashMap<>();


    private RectF mRegionData;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_adjust_crop);
        initTransformData();
        initView();
        initData();
        initListener();
        initStreamingContextListener();
        if (mTimeline != null) {
            mTotalDurationView.setText(FormatUtils.microsecond2Time(mTimeline.getDuration()));
        }
    }

    private void initData() {
        mClipArrayList = BackupData.instance().cloneClipInfoData();
        mCurClipIndex = BackupData.instance().getClipIndex();
        mOriginalTimelineWidth = TimelineData.instance().getVideoResolution().imageWidth;
        mOriginalTimelineHeight = TimelineData.instance().getVideoResolution().imageHeight;
        if (mCurClipIndex < 0 || mCurClipIndex >= mClipArrayList.size()) {
            return;
        }
        ClipInfo clipInfo = mClipArrayList.get(mCurClipIndex);
        int scaleX = clipInfo.getScaleX();
        int scaleY = clipInfo.getScaleY();
        if (scaleX >= -1) {
            mScaleX = scaleX;
        }
        if (scaleY >= -1) {
            mScaleY = scaleY;
        }
        mRotateAngle = clipInfo.getRotateAngle();
        mTimeline = TimelineUtil.createSingleClipTimeline(clipInfo, true);
        //注意顺序 Pay attention to the order
        TimelineUtil.buildTimelineBackground(mTimeline, mClipArrayList);
        TimelineUtil.buildTimelineMaskClipInfo(mTimeline, mClipArrayList);
        NvsVideoResolution resolution = TimelineUtil.getVideoEditResolutionByClip(clipInfo.getFilePath(), 1080);
        mTimeline.changeVideoSize(resolution.imageWidth, resolution.imageHeight);
        if (mTimeline == null) {
            return;
        }
        NvsVideoTrack videoTrack = mTimeline.getVideoTrackByIndex(0);
        if (videoTrack == null) {
            return;
        }
        mVideoClip = videoTrack.getClipByIndex(0);
        if (mVideoClip == null) {
            return;
        }
        TimelineUtil.removeRawBuildInFx(mVideoClip, NvsConstants.CUT_KEY_MASK_GENERATOR_TRANSFORM_2D);
        mCutData = getInitCutData();
        setCutData(mCutData);
        mCuttingMenuView.setProgress(mCutData.getTransformData(StoryboardUtil.STORYBOARD_KEY_ROTATION_Z));
        mCuttingMenuView.setSelectRatio(mCutData.getRatio());
        adjustClip();
    }

    private void initView() {
        mLiveWindow = findViewById(R.id.liveWindow);
        mLiveWindow.setFillMode(NvsLiveWindow.FILLMODE_PRESERVEASPECTCROP);
        mPlayerLayout = findViewById(R.id.video_layout);
        mCutView = findViewById(R.id.cut_view);
        mPlayButton = findViewById(R.id.playLayout);
        mCurrentPlaytimeView = findViewById(R.id.currentPlaytime);
        mSeekBar = findViewById(R.id.play_seekBar);
        mPlayButtonImage = findViewById(R.id.playImage);
        mTotalDurationView = findViewById(R.id.totalDuration);
        mCuttingMenuView = findViewById(R.id.edit_cutting_menu_view);
        mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
    }

    private void initListener() {
        mPlayButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (getCurrentEngineState() == NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
                    stopEngine();
                } else {
                    if (mTimeline == null) {
                        return;
                    }
                    long startTime = mStreamingContext.getTimelineCurrentPosition(mTimeline);
                    long alreadyPlayDuration = startTime - mStartTime;//已经在指定片段上播放的时长
                    long endTime = startTime + getDuration() - (alreadyPlayDuration);
                    playVideo(startTime, endTime);
                }
            }
        });
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            private long currentTime = 0L;

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser) {
                    currentTime = getDuration() * progress / 100 + mStartTime; //绝对时间
                    seekTimeline(currentTime, 0);
                    updateCurPlayTime(currentTime);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                playVideo(currentTime, currentTime + getDuration());
            }
        });
        final boolean[] updateView = {false};
        mCutView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (!updateView[0]) {
                    Point size = getOriginalLiveWindowLayoutParam();
                    setCutRectViewSize(size);
                    setLiveWindowSize(size);
                    new Handler().post(new Runnable() {
                        @Override
                        public void run() {
                            initLiveWindowCenterPoint();
                            Point size = changeCutRectViewNoScale(mRatio);
                            mMinLiveWindowScale = getSuitLiveWindowScale(size);
                            mTransformData = parseToViewTransData(mTransformData);
                            mLiveWindow.setRotation(mTransformData.get(STORYBOARD_KEY_ROTATION_Z));
                            scaleLiveWindow(mTransformData.get(STORYBOARD_KEY_SCALE_X));
                            mLiveWindow.setTranslationX(mTransformData.get(STORYBOARD_KEY_TRANS_X));
                            mLiveWindow.setTranslationY(mTransformData.get(STORYBOARD_KEY_TRANS_Y));

                            FloatPoint pointLT = new FloatPoint();
                            FloatPoint pointRB = new FloatPoint();
                            float halfWidth = mLiveWindow.getWidth() * 1.0F / 2;
                            float halfHeight = mLiveWindow.getHeight() * 1.0F / 2;
                            pointLT.x = mCenterPoint.x - halfWidth;
                            pointLT.y = mCenterPoint.y - halfHeight;

                            pointRB.x = mCenterPoint.x + halfWidth;
                            pointRB.y = mCenterPoint.y + halfHeight;

                            float oldScale = mTransformData.get(STORYBOARD_KEY_SCALE_X);
                            float degree = mTransformData.get(STORYBOARD_KEY_ROTATION_Z);
                            pointLT = transformData(pointLT, mCenterPoint, oldScale, degree,
                                    mTransformData.get(STORYBOARD_KEY_TRANS_X), mTransformData.get(STORYBOARD_KEY_TRANS_Y));
                            pointRB = transformData(pointRB, mCenterPoint, oldScale, degree,
                                    mTransformData.get(STORYBOARD_KEY_TRANS_X), mTransformData.get(STORYBOARD_KEY_TRANS_Y));

                            mCenterPoint.x = (pointLT.x + pointRB.x) / 2;
                            mCenterPoint.y = (pointLT.y + pointRB.y) / 2;
                            mRegionData = updateRegionData();

                            Rect clipRect = getClipRect(0, 0, 0);
                            mCutView.setLimitRect(clipRect);
                        }
                    });
                    updateView[0] = true;
                }
            }
        });
        connectTimelineWithLiveWindow();
        mCutView.setOnTransformListener(new CutRectLayout.OnTransformListener() {
            @Override
            public void onTrans(float deltaX, float deltaY) {
                if (deltaX == 0 || deltaY == 0) {
                    return;
                }
                float oldTransX = mTransformData.get(STORYBOARD_KEY_TRANS_X);
                float oldTransY = mTransformData.get(STORYBOARD_KEY_TRANS_Y);
                float newTransX = oldTransX;
                float newTransY = oldTransY;
                if (canTrans(deltaX, 0, 0F)) {
                    newTransX = oldTransX - deltaX;
                    mCenterPoint.x -= deltaX;
                }
                if (canTrans(0, deltaY, 0F)) {
                    newTransY = oldTransY - deltaY;
                    mCenterPoint.y -= deltaY;
                }
                mLiveWindow.setTranslationX(newTransX);
                mLiveWindow.setTranslationY(newTransY);
                mTransformData.put(STORYBOARD_KEY_TRANS_X, newTransX);
                mTransformData.put(STORYBOARD_KEY_TRANS_Y, newTransY);
                Rect clipRect = getClipRect(0, 0, 0);
                mCutView.setLimitRect(clipRect);
            }

            @Override
            public void onTransUp() {

            }

            @Override
            public void onScaleAndRotate(float scale, float degree) {
                if (scale < 1.0F && !canTrans(0, 0, -degree)) {
                    return;
                }

                float newDegree = mTransformData.get(STORYBOARD_KEY_ROTATION_Z) - degree;
                if (newDegree > 45 && degree < 0) {
                    return;
                }
                if (newDegree < -45 && degree > 0) {
                    return;
                }
                newDegree = (int) newDegree;
                if (!hasVibrator && newDegree == 0) {
                    if (mVibrator != null) {
                        mVibrator.vibrate(50);
                    } //震动一下
                    hasVibrator = true;
                }
                if (newDegree != 0) {
                    hasVibrator = false;
                }
                mLiveWindow.setRotation(newDegree);
                mTransformData.put(STORYBOARD_KEY_ROTATION_Z, newDegree);

                double scaleValue = computeScale(newDegree, mTransformData.get(STORYBOARD_KEY_SCALE_X) * scale);
                double newScale = mTransformData.get(STORYBOARD_KEY_SCALE_X) * scale;
                if (newScale < scaleValue && scaleValue > 1.0F) {
                    newScale = (float) scaleValue;
                }

                if (scaleValue != 1.0F && scale < 1.0F) {
                    return;
                }

                if (newScale < mMinLiveWindowScale) {
                    newScale = mMinLiveWindowScale;
                }
                scaleLiveWindow((float) newScale);
                mTransformData.put(STORYBOARD_KEY_SCALE_X, (float) newScale);
                mTransformData.put(STORYBOARD_KEY_SCALE_Y, (float) newScale);
                mCuttingMenuView.setProgress(newDegree);
                Rect clipRect = getClipRect(0, 0, 0);
                mCutView.setLimitRect(clipRect);
                mTempScale = mTransformData.get(STORYBOARD_KEY_SCALE_X);
            }

            @Override
            public void onTransEnd(float scale, float[] size) {
                if (scale < 0) {
                    rotateVideo(mTransformData.get(STORYBOARD_KEY_ROTATION_Z));
                } else {
                    float newScale = mTransformData.get(STORYBOARD_KEY_SCALE_X) * scale;
                    scaleLiveWindow(newScale);
                    mTransformData.put(STORYBOARD_KEY_SCALE_X, newScale);
                    mTransformData.put(STORYBOARD_KEY_SCALE_Y, newScale);
                }
                Rect clipRect = getClipRect(0, 0, 0);
                mCutView.setLimitRect(clipRect);
            }

            @Override
            public void onRectMoved(float scale, Point distance, Point anchor) {
                if (mOriginalSize == null) {
                    mOriginalSize = new Point();
                }
                mOriginalSize.x = mCutView.getRectWidth();
                mOriginalSize.y = mCutView.getRectHeight();
                mMinLiveWindowScale = getSuitLiveWindowScale(mOriginalSize);

                float newScale = mTransformData.get(STORYBOARD_KEY_SCALE_X) * scale;
                //double minScale = computeScale(mTransformData.get(STORYBOARD_KEY_ROTATION_Z), scale);
               /* if (newScale < minScale) {
                    newScale = (float) minScale;
                    scale = newScale / mTransformData.get(STORYBOARD_KEY_SCALE_X);
                }*/
                scaleLiveWindow(newScale);

                FloatPoint pointAfter = new FloatPoint();
                pointAfter.x = anchor.x;
                pointAfter.y = anchor.y;
                transformData(pointAfter, mCenterPoint, scale, 0);

                float deltaX = anchor.x - pointAfter.x + distance.x;
                float deltaY = anchor.y - pointAfter.y + distance.y;
                translateLiveWindow(deltaX, deltaY);
                mRegionData = updateRegionData();

                mRatioValue = mCutView.getRectWidth() * 1F / mCutView.getRectHeight();

                Rect clipRect = getClipRect(0, 0, 0);
                mCutView.setLimitRect(clipRect);
                mTempScale = mTransformData.get(STORYBOARD_KEY_SCALE_X);
            }

            @Override
            public boolean canMove(Point anchor) {
                if (mTransformData.get(STORYBOARD_KEY_ROTATION_Z) == 0) {
                    return true;
                }
                return canTrans(anchor);
            }
        });
        mCuttingMenuView.setOnSeekBarListener(new MYSeekBarView.OnSeekBarListener() {
            @Override
            public void onStopTrackingTouch(int progress, String name) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser) {
                    rotateVideo(progress - 45);
                }
            }
        });
        mCuttingMenuView.setOnConfrimListener(() -> {
            Map<String, Float> transFromData = getTransFromData(mOriginalTimelineWidth, mOriginalTimelineHeight);
            parseTransToTimeline(mOriginalTimelineWidth, mOriginalTimelineHeight, mVideoClip.getFilePath(),
                    getRectViewSize(), transFromData);

            NvsPanAndScan panAndScan = mVideoClip.getPanAndScan();
            mClipArrayList.get(mCurClipIndex).setPan(panAndScan.pan);
            mClipArrayList.get(mCurClipIndex).setScan(panAndScan.scan);
            mCutData.setTransformData(transFromData);
            mCutData.setRatio(mRatio);
            mCutData.setRatioValue(mRatioValue);
            mCutData.setmRegionData(getRegionData(new float[]{1F, 1F}));
            mClipArrayList.get(mCurClipIndex).setCropInfo(mCutData);
            BackupData.instance().setClipInfoData(mClipArrayList);
            TimelineData.instance().setClipInfoData(mClipArrayList);
            removeTimeline();
            Intent intent = new Intent();
            setResult(RESULT_OK, intent);
            finish();
        });
        mCuttingMenuView.setOnRatioSelectListener(new CuttingMenuView.OnRatioSelectListener() {
            @Override
            public void onItemClicked(int ratio) {
                changeCutRectView(ratio);
            }

            @Override
            public void onReset() {
                mScaleX = 1;
                mScaleY = 1;
                mRotateAngle = 0;
                rotateClip();
                reset();
                mCuttingMenuView.setProgress(0);
                mVideoClip.setPanAndScan(0.0f, 0.0f);
                mVideoFx.setFloatVal(Constants.FX_TRANSFORM_2D_SCALE_X, mScaleX);
                mVideoFx.setFloatVal(Constants.FX_TRANSFORM_2D_SCALE_Y, mScaleY);
                mClipArrayList.get(mCurClipIndex).setScaleX(mScaleX);
                mClipArrayList.get(mCurClipIndex).setScaleY(mScaleY);
                mClipArrayList.get(mCurClipIndex).setRotateAngle(mRotateAngle);
                if (getCurrentEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
                    seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
                }
            }

            @Override
            public void onVertical() {
                mScaleY = mScaleY > 0 ? -1 : 1;
                mVideoFx.setFloatVal(Constants.FX_TRANSFORM_2D_SCALE_Y, mScaleY);
                mClipArrayList.get(mCurClipIndex).setScaleY(mScaleY);
                if (getCurrentEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
                    seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
                }
            }

            @Override
            public void onRotation() {
                int rotateAngle = mVideoClip.getExtraVideoRotation();
                switch (rotateAngle) {
                    case NvsVideoClip.ClIP_EXTRAVIDEOROTATION_0:
                        mRotateAngle = NvsVideoClip.ClIP_EXTRAVIDEOROTATION_90;
                        break;
                    case NvsVideoClip.ClIP_EXTRAVIDEOROTATION_90:
                        mRotateAngle = NvsVideoClip.ClIP_EXTRAVIDEOROTATION_180;
                        break;
                    case NvsVideoClip.ClIP_EXTRAVIDEOROTATION_180:
                        mRotateAngle = NvsVideoClip.ClIP_EXTRAVIDEOROTATION_270;
                        break;
                    case NvsVideoClip.ClIP_EXTRAVIDEOROTATION_270:
                        mRotateAngle = NvsVideoClip.ClIP_EXTRAVIDEOROTATION_0;
                        break;
                    default:
                        break;
                }
                rotateClip();
                mClipArrayList.get(mCurClipIndex).setRotateAngle(mRotateAngle);
                if (getCurrentEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
                    seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
                }
            }

            @Override
            public void onHorizontal() {
                mScaleX = mScaleX > 0 ? -1 : 1;
                mVideoFx.setFloatVal(Constants.FX_TRANSFORM_2D_SCALE_X, mScaleX);
                mClipArrayList.get(mCurClipIndex).setScaleX(mScaleX);
                if (getCurrentEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
                    seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
                }
            }
        });

    }

    private void initStreamingContextListener() {

        mStreamingContext.setPlaybackCallback(new NvsStreamingContext.PlaybackCallback() {
            @Override
            public void onPlaybackPreloadingCompletion(NvsTimeline nvsTimeline) {

            }

            @Override
            public void onPlaybackStopped(NvsTimeline nvsTimeline) {
            }

            @Override
            public void onPlaybackEOF(NvsTimeline nvsTimeline) {
                mHandler.sendEmptyMessage(MESSAGE_RESET_PLAY_BACK_STATE);
            }
        });

        mStreamingContext.setPlaybackCallback2(new NvsStreamingContext.PlaybackCallback2() {
            @Override
            public void onPlaybackTimelinePosition(NvsTimeline nvsTimeline, long cur_position) {
                updatePlayProgress(cur_position);
            }
        });
        mStreamingContext.setSeekingCallback(new NvsStreamingContext.SeekingCallback() {
            @Override
            public void onSeekingTimelinePosition(NvsTimeline nvsTimeline, long cur_position) {
                updatePlayProgress(cur_position);
            }
        });
        mStreamingContext.setStreamingEngineCallback(new NvsStreamingContext.StreamingEngineCallback() {
            @Override
            public void onStreamingEngineStateChanged(int i) {
                if (i == NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
                    mPlayButtonImage.setBackgroundResource(R.mipmap.icon_pause);
                } else {
                    mPlayButtonImage.setBackgroundResource(R.mipmap.icon_play);
                }
            }

            @Override
            public void onFirstVideoFramePresented(NvsTimeline nvsTimeline) {

            }
        });
    }

    public void reset() {
        mLiveWindow.setTranslationX(0);
        mLiveWindow.setTranslationY(0);
        mLiveWindow.setRotation(0);
        scaleLiveWindow(1.0F);
        setLiveWindowRatio(NvsConstants.AspectRatio.AspectRatio_NoFitRatio);
        mOriginalSize = null;
        mTransformData.put(STORYBOARD_KEY_ROTATION_Z, 0F);
        mTransformData.put(STORYBOARD_KEY_TRANS_X, 0F);
        mTransformData.put(STORYBOARD_KEY_TRANS_Y, 0F);
        initLiveWindowCenterPoint();
        mRatio = NvsConstants.AspectRatio.AspectRatio_NoFitRatio;
        mMinLiveWindowScale = 1.0F;
        mRegionData = updateRegionData();
        Rect clipRect = getClipRect(0, 0, 0);
        mCutView.setLimitRect(clipRect);
        mTempScale = 1.0F;
        mRatioValue = mCutView.getRectWidth() * 1F / mCutView.getRectHeight();
    }

    private void setLiveWindowRatio(int ratio) {
        if (null == mTimeline) {
            return;
        }
        Point size;
        if (ratio == NvsConstants.AspectRatio.AspectRatio_NoFitRatio) {
            size = getOriginalLiveWindowLayoutParam();
        } else {
            size = getLiveWindowSizeByRatio(ratio);
        }
        setLiveWindowSize(size);
        setCutRectViewSize(size);
        if (ratio == NvsConstants.AspectRatio.AspectRatio_NoFitRatio) {
            mCutView.setWidthHeightRatio(-1);
        } else {
            mCutView.setWidthHeightRatio(size.x * 1.0F / size.y);
        }
    }

    public void changeCutRectView(int ratio) {
        //LiveWindow和 CutRectView需要分别设置，
        // LiveWindow适配CutRectView大小，需要和CutRectView宽和高对齐，
        // 如果LiveWindow宽或高有一边没有对齐，LiveWindow需要进行缩放
        Point size;
        if (ratio == NvsConstants.AspectRatio.AspectRatio_NoFitRatio) {
            if (mOriginalSize == null) {
                size = getOriginalLiveWindowLayoutParam();
                mOriginalSize = size;
            } else {
                size = mOriginalSize;
            }
        } else {
            size = getLiveWindowSizeByRatio(ratio);
        }
        mRatioValue = size.x * 1F / size.y;
        setCutRectViewSize(size);
        if (ratio == NvsConstants.AspectRatio.AspectRatio_NoFitRatio) {
            mCutView.setWidthHeightRatio(-1);
        } else {
            mCutView.setWidthHeightRatio(size.x * 1.0F / size.y);
        }
        mMinLiveWindowScale = getSuitLiveWindowScale(size);
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mRegionData = updateRegionData();
                float rotation = mTransformData.get(STORYBOARD_KEY_ROTATION_Z);
                if (rotation == 0) {
                    float oldScale = mTransformData.get(STORYBOARD_KEY_SCALE_X);
                    if (mMinLiveWindowScale > oldScale) {
                        scaleLiveWindow(mMinLiveWindowScale);
                    } else {
                        rotateVideo(rotation);
                    }
                } else {
                    rotateVideo(rotation);
                }
                Rect clipRect = getClipRect(0, 0, 0);
                mCutView.setLimitRect(clipRect);
                mTempScale = mTransformData.get(STORYBOARD_KEY_SCALE_X);
            }
        });
        mRatio = ratio;
    }

    public float[] getRegionData(float[] size) {
        if (mRegionData == null) {
            return null;
        }
        RectF rectF = new RectF();
        rectF.top = mRegionData.top * size[1];
        rectF.bottom = mRegionData.bottom * size[1];
        rectF.left = mRegionData.left * size[0];
        rectF.right = mRegionData.right * size[0];
        float[] regionData;
        if (mRatio == NvsConstants.AspectRatio.AspectRatio_NoFitRatio) {
            regionData = new float[10];
            regionData[0] = rectF.left;
            regionData[1] = rectF.top;
            regionData[2] = rectF.right;
            regionData[3] = rectF.top;
            regionData[4] = rectF.right;
            regionData[5] = rectF.bottom;
            regionData[6] = rectF.left;
            regionData[7] = rectF.bottom;
            regionData[8] = rectF.left;
            regionData[9] = 0;
        } else {
            regionData = new float[8];
            regionData[0] = rectF.left;
            regionData[1] = rectF.top;
            regionData[2] = rectF.right;
            regionData[3] = rectF.top;
            regionData[4] = rectF.right;
            regionData[5] = rectF.bottom;
            regionData[6] = rectF.left;
            regionData[7] = rectF.bottom;
        }
        return regionData;
    }

    public int[] getRectViewSize() {
        return new int[]{mCutView.getRectWidth(), mCutView.getRectHeight()};
    }

    /**
     * 转换transform 数据为timeline范围内的transfrom
     * Convert the transform data to the transform within the timeline range
     *
     * @param timelineWidth  时间线的宽
     * @param timelineHeight 时间线的高
     * @param filePath       文件路径
     * @param rectSize       裁剪区域的宽高
     * @param transFormData  转换数据
     * @return
     */
    public Map<String, Float> parseTransToTimeline(int timelineWidth, int timelineHeight, String filePath, int[] rectSize, Map<String, Float> transFormData) {
        float transXInView = transFormData.get(StoryboardUtil.STORYBOARD_KEY_TRANS_X);
        float transYInView = transFormData.get(StoryboardUtil.STORYBOARD_KEY_TRANS_Y);
        NvsAVFileInfo avFileInfo = mStreamingContext.getAVFileInfo(filePath);
        int videoStreamRotation = avFileInfo.getVideoStreamRotation(0);
        NvsSize dimension = avFileInfo.getVideoStreamDimension(0);
        int height;
        int width;
        if (videoStreamRotation % 2 == 0) {
            height = dimension.height;
            width = dimension.width;
        } else {
            width = dimension.height;
            height = dimension.width;
        }

        float fileRatio = width * 1F / height;
        float timelineRatio = timelineWidth * 1F / timelineHeight;

        float fileWidthInTimeline;
        float fileHeightInTimeline;
        if (fileRatio > timelineRatio) {
            //文件宽对齐 File width alignment
            fileWidthInTimeline = timelineWidth;
            fileHeightInTimeline = fileWidthInTimeline / fileRatio;
        } else {//高对齐 High alignment
            fileHeightInTimeline = timelineHeight;
            fileWidthInTimeline = fileHeightInTimeline * fileRatio;
        }
        float rectWidthInTimeline;
        float rectHeightInTimeline;
        float rectRatio = rectSize[0] * 1F / rectSize[1];
        if (rectRatio > fileRatio) {
            //裁剪区域宽对齐 Cropped area width alignment
            rectWidthInTimeline = fileWidthInTimeline;
            rectHeightInTimeline = rectWidthInTimeline / rectRatio;
        } else {
            rectHeightInTimeline = fileHeightInTimeline;
            rectWidthInTimeline = rectHeightInTimeline * rectRatio;
        }

        float transXInTimeline = transXInView / rectSize[0] * rectWidthInTimeline;
        float transYInTimeline = transYInView / rectSize[1] * rectHeightInTimeline;
        //Mask Generator方案需要归一化
        transFormData.put(StoryboardUtil.STORYBOARD_KEY_TRANS_X, transXInTimeline / fileWidthInTimeline * 2);
        transFormData.put(StoryboardUtil.STORYBOARD_KEY_TRANS_Y, -transYInTimeline / fileHeightInTimeline * 2);//Timeline 坐标轴反向
        return transFormData;
    }

    public Map<String, Float> getTransFromData(int originalTimelineWidth, int originalTimelineHeight) {
        return parseToTimelineTransData(originalTimelineWidth, originalTimelineHeight);
    }

    private Map<String, Float> parseToTimelineTransData(int originalTimelineWidth, int originalTimelineHeight) {
        Map<String, Float> result = new HashMap<>();
        float realScale = (float) mTransformData.get(STORYBOARD_KEY_SCALE_X) / mMinLiveWindowScale;
        float realTransX = (float) mTransformData.get(STORYBOARD_KEY_TRANS_X);
        float realTransY = (float) mTransformData.get(STORYBOARD_KEY_TRANS_Y);
        result.put(STORYBOARD_KEY_SCALE_X, realScale);
        result.put(STORYBOARD_KEY_SCALE_Y, realScale);
        result.put(STORYBOARD_KEY_TRANS_X, realTransX);
        result.put(STORYBOARD_KEY_TRANS_Y, realTransY);
        result.put(STORYBOARD_KEY_ROTATION_Z, (float) mTransformData.get(STORYBOARD_KEY_ROTATION_Z));
        return result;
    }

    /**
     * 旋转VideoClip
     * rotate the VideoClip
     */
    private void rotateClip() {
        mVideoClip.setExtraVideoRotation(mRotateAngle);
    }

    private void adjustClip() {
        rotateClip();
        int fxCount = mVideoClip.getFxCount();
        for (int index = 0; index < fxCount; ++index) {
            NvsVideoFx videoFx = mVideoClip.getFxByIndex(index);
            if (videoFx == null) {
                continue;
            }
            if (videoFx.getBuiltinVideoFxName().compareTo(Constants.FX_TRANSFORM_2D) == 0) {
                mVideoFx = videoFx;
                break;
            }
        }
        if (mVideoFx == null) {
            mVideoFx = mVideoClip.appendRawBuiltinFx(Constants.FX_TRANSFORM_2D);
        }
        if (mVideoFx == null) {
            return;
        }
        if (mScaleX >= -1) {
            mVideoFx.setFloatVal(Constants.FX_TRANSFORM_2D_SCALE_X, mScaleX);
        }
        if (mScaleY >= -1) {
            mVideoFx.setFloatVal(Constants.FX_TRANSFORM_2D_SCALE_Y, mScaleY);
        }
    }

    /**
     * 获取裁剪信息
     * Get cropping information
     *
     * @return
     */
    private CutData getInitCutData() {
        CutData cutData = mClipArrayList.get(mCurClipIndex).getCropInfo();
        if (cutData == null) {
            cutData = new CutData();
        }
        return cutData;
    }


    private Point getOriginalLiveWindowLayoutParam() {
        if (mTimeline == null) {
            throw new NullPointerException("mTimeline == null");
        }
        NvsVideoResolution videoRes = mTimeline.getVideoRes();
        Point size = new Point();
        int screenWidth = mPlayerLayout.getWidth();
        int newHeight = mPlayerLayout.getHeight();
        int imageWidth = videoRes.imageWidth;
        int imageHeight = videoRes.imageHeight;
        float viewRatio = screenWidth * 1.0F / newHeight;
        float timelineRation = imageWidth * 1.0F / imageHeight;
        float ratio = 1.0F;
        if (timelineRation > viewRatio) {//宽对齐
            size.x = screenWidth;
            ratio = screenWidth * 1.0F / imageWidth;
            size.y = (int) (ratio * imageHeight);
        } else {//高对齐
            size.y = newHeight;
            ratio = newHeight * 1.0F / imageHeight;
            size.x = (int) (ratio * imageWidth);
        }
        return size;
    }


    private void setCutRectViewSize(Point size) {
        mCutView.setDrawRectSize(size.x, size.y);
    }


    private void setLiveWindowSize(Point size) {
        ViewGroup.LayoutParams layoutParams = mLiveWindow.getLayoutParams();
        layoutParams.width = size.x;
        layoutParams.height = size.y;
        mLiveWindow.setLayoutParams(layoutParams);
    }


    private void initLiveWindowCenterPoint() {
        int[] location = new int[2];
        mLiveWindow.getLocationOnScreen(location);
        int locationX = location[0];
        int locationY = location[1];
        mCenterPoint.x = locationX + mLiveWindow.getWidth() * 1.0F / 2;
        mCenterPoint.y = locationY + mLiveWindow.getHeight() * 1.0F / 2;
    }

    private FloatPoint transformData(FloatPoint point, FloatPoint centerPoint, float scale, float degree) {
        float[] src = new float[]{point.x, point.y};
        Matrix matrix = new Matrix();
        matrix.setRotate(degree, centerPoint.x, centerPoint.y);
        matrix.mapPoints(src);
        matrix.setScale(scale, scale, centerPoint.x, centerPoint.y);
        matrix.mapPoints(src);
        point.x = Math.round(src[0]);
        point.y = Math.round(src[1]);
        return point;
    }

    public void rotateVideo(float degree) {
        mLiveWindow.setRotation(degree * 1.0F);
        mTransformData.put(STORYBOARD_KEY_ROTATION_Z, degree);
        float scaleValue = (float) computeScale(degree, mTempScale);
        //不能小于当前缩放的值 Cannot be less than the current zoom value.
        if (scaleValue < mTempScale) {
            scaleValue = mTempScale;
        }
        scaleLiveWindow(scaleValue);
        if (degree != 0) {
            mCutView.setLimitRect(null);
        }
    }

    /**
     * 计算缩放值
     *
     * @param newDegree 最新的角度值
     * @param newScale  最新的缩放值
     * @return 缩放值，这个值是相对于原始值的缩放值
     */
    private double computeScale(float newDegree, float newScale) {
        //获取4个角的顶点数据， 并进行坐标转换
        FloatPoint pointLT = new FloatPoint();
        FloatPoint pointLB = new FloatPoint();
        FloatPoint pointRT = new FloatPoint();
        FloatPoint pointRB = new FloatPoint();

        float halfWidth = mLiveWindow.getWidth() * 1.0F / 2;
        float halfHeight = mLiveWindow.getHeight() * 1.0F / 2;

        pointLT.x = mCenterPoint.x - halfWidth;
        pointLT.y = mCenterPoint.y - halfHeight;

        pointLB.x = mCenterPoint.x - halfWidth;
        pointLB.y = mCenterPoint.y + halfHeight;

        pointRT.x = mCenterPoint.x + halfWidth;
        pointRT.y = mCenterPoint.y - halfHeight;

        pointRB.x = mCenterPoint.x + halfWidth;
        pointRB.y = mCenterPoint.y + halfHeight;

        pointLT = transformData(pointLT, mCenterPoint, newScale, newDegree);
        pointLB = transformData(pointLB, mCenterPoint, newScale, newDegree);
        pointRT = transformData(pointRT, mCenterPoint, newScale, newDegree);
        pointRB = transformData(pointRB, mCenterPoint, newScale, newDegree);

        int[] location = new int[2];
        mCutView.getLocationOnScreen(location);
        int rectLeft = location[0] + mCutView.getDrawRectViewLeft();
        int rectTop = location[1] + mCutView.getDrawRectViewTop();
        int rectRight = rectLeft + mCutView.getRectWidth();
        int rectBottom = rectTop + mCutView.getRectHeight();


        //左上角
        FloatPoint point = new FloatPoint();
        point.x = rectLeft;
        point.y = rectTop;

        boolean inRect = isInRect(pointLT, pointRT, pointRB, pointLB, point);
        double scale = 1.0d;
        if (!inRect) {
            //计算左上角的点到矩形上边的距离 Calculates the distance from the point in the upper left corner to the upper edge of the rectangle
            double cos = Math.cos(Math.toRadians(90 - angle(mCenterPoint, point, pointLT, pointRT)));
            float centerToPoint = lineSpace(mCenterPoint.x, mCenterPoint.y, point.x, point.y);
            double pointToLineLR = centerToPoint * cos;

            //计算左上角的点到矩形左边的距离 Calculate the distance from the point in the upper left corner to the left of the rectangle
            cos = Math.cos(Math.toRadians(90 - angle(mCenterPoint, point, pointLT, pointLB)));
            double pointToLineTB = centerToPoint * cos;

            double scaleX = pointToLineTB / halfWidth;
            double scaleY = pointToLineLR / halfHeight;
            scale = Math.max(scaleX, scaleY);
            if (Double.isNaN(scale)) {
                scale = 1.0d;
            }
        }
        double scaleTemp = scale;

        //右上角
        point = new FloatPoint();
        point.x = rectRight;
        point.y = rectTop;

        inRect = isInRect(pointLT, pointRT, pointRB, pointLB, point);
        if (!inRect) {
            //计算右上角的点到矩形右边的距离 Calculate the distance from the point in the upper right corner to the right of the rectangle
            double cos = Math.cos(Math.toRadians(90 - angle(mCenterPoint, point, pointRT, pointRB)));
            float centerToPoint = lineSpace(mCenterPoint.x, mCenterPoint.y, point.x, point.y);
            double pointToLineTB = centerToPoint * cos;
            //计算右上角的点到矩形上边的距离 Calculate the distance from the point in the upper right corner to the upper edge of the rectangle
            cos = Math.cos(Math.toRadians(90 - angle(mCenterPoint, point, pointRT, pointLT)));
            double pointToLineRL = centerToPoint * cos;

            double scaleX = pointToLineRL / halfHeight;
            double scaleY = pointToLineTB / halfWidth;
            scaleTemp = Math.max(scaleX, scaleY);
            if (Double.isNaN(scaleTemp)) {
                scaleTemp = 1.0d;
            }
        }
        scale = Math.max(scale, scaleTemp);

        //右下角
        point = new FloatPoint();
        point.x = rectRight;
        point.y = rectBottom;

        inRect = isInRect(pointLT, pointRT, pointRB, pointLB, point);

        if (!inRect) {
            //计算右下角的点到矩形下边的距离 Calculate the distance from the point in the lower right corner to the bottom of the rectangle
            double cos = Math.cos(Math.toRadians(90 - angle(mCenterPoint, point, pointRB, pointLB)));
            float centerToPoint = lineSpace(mCenterPoint.x, mCenterPoint.y, point.x, point.y);
            double pointToLineRL = centerToPoint * cos;
            //计算右下角的点到矩形右边的距离 Calculate the distance from the point in the lower right corner to the right of the rectangle
            cos = Math.cos(Math.toRadians(90 - angle(mCenterPoint, point, pointRB, pointRT)));
            double pointToLineTB = centerToPoint * cos;

            double scaleX = pointToLineRL / halfHeight;
            double scaleY = pointToLineTB / halfWidth;
            scaleTemp = Math.max(scaleX, scaleY);
            if (Double.isNaN(scaleTemp)) {
                scaleTemp = 1.0d;
            }
        }
        scale = Math.max(scale, scaleTemp);

        //左下角
        point = new FloatPoint();
        point.x = rectLeft;
        point.y = rectBottom;

        inRect = isInRect(pointLT, pointRT, pointRB, pointLB, point);
        if (!inRect) {
            //计算左下角的点到矩形下边的距离 Calculate the distance from the point in the lower left corner to the bottom of the rectangle
            double cos = Math.cos(Math.toRadians(90 - angle(mCenterPoint, point, pointLB, pointRB)));
            float centerToPoint = lineSpace(mCenterPoint.x, mCenterPoint.y, point.x, point.y);
            double pointToLineLR = centerToPoint * cos;
            //计算左下角的点到矩形左边的距离 Calculate the distance from the point in the lower left corner to the left side of the rectangle
            cos = Math.cos(Math.toRadians(90 - angle(mCenterPoint, point, pointLT, pointLB)));
            double pointToLineTB = centerToPoint * cos;

            double scaleX = pointToLineLR / halfHeight;
            double scaleY = pointToLineTB / halfWidth;
            scaleTemp = Math.max(scaleX, scaleY);
            if (Double.isNaN(scaleTemp)) {
                scaleTemp = 1.0d;
            }
        }
        scale = Math.max(scale, scaleTemp);
        return scale;
    }


    /**
     * 计算向量夹角，此计算值为锐角
     *
     * @param pointA1 点A1
     * @param pointA2 点A2
     * @param pointB1 点B1
     * @param pointB2 点B2
     * @return the angle
     */
    private static double angle(FloatPoint pointA1, FloatPoint pointA2, FloatPoint pointB1, FloatPoint pointB2) {
        float x1 = pointA1.x - pointA2.x;
        float y1 = pointA1.y - pointA2.y;
        float x2 = pointB1.x - pointB2.x;
        float y2 = pointB1.y - pointB2.y;
        double value = (x1 * x2 + y1 * y2) / (Math.sqrt(x1 * x1 + y1 * y1) * Math.sqrt(x2 * x2 + y2 * y2)); // 余弦值
        return Math.toDegrees(Math.acos(Math.abs(value)));// 角度
    }


    // 计算两点之间的距离
    private float lineSpace(float x1, float y1, float x2, float y2) {
        float lineLength = (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2)
                * (y1 - y2));
        return lineLength;
    }

    private boolean canTrans(Point size) {
        //获取4个角的顶点数据，并进行坐标转换
        FloatPoint pointLT = new FloatPoint();
        FloatPoint pointLB = new FloatPoint();
        FloatPoint pointRT = new FloatPoint();
        FloatPoint pointRB = new FloatPoint();

        float halfWidth = mLiveWindow.getWidth() * 1.0F / 2;
        float halfHeight = mLiveWindow.getHeight() * 1.0F / 2;

        FloatPoint centerPoint = new FloatPoint();
        centerPoint.x = mCenterPoint.x;
        centerPoint.y = mCenterPoint.y;

        pointLT.x = centerPoint.x - halfWidth;
        pointLT.y = centerPoint.y - halfHeight;

        pointLB.x = centerPoint.x - halfWidth;
        pointLB.y = centerPoint.y + halfHeight;

        pointRT.x = centerPoint.x + halfWidth;
        pointRT.y = centerPoint.y - halfHeight;

        pointRB.x = centerPoint.x + halfWidth;
        pointRB.y = centerPoint.y + halfHeight;

        float degree = mTransformData.get(STORYBOARD_KEY_ROTATION_Z);
        float scale = mTransformData.get(STORYBOARD_KEY_SCALE_X);
        pointLT = transformData(pointLT, centerPoint, scale, degree);
        pointLB = transformData(pointLB, centerPoint, scale, degree);
        pointRT = transformData(pointRT, centerPoint, scale, degree);
        pointRB = transformData(pointRB, centerPoint, scale, degree);

        //判断四个顶点是否超出LiveWindow坐标范围外
        int[] location = new int[2];
        mCutView.getLocationOnScreen(location);
        int rectLeft = location[0] + mCutView.getDrawRectViewLeft();
        int rectTop = location[1] + mCutView.getDrawRectViewTop();
        int rectRight = rectLeft + size.x;
        int rectBottom = rectTop + size.y;
        //左上角
        FloatPoint point = new FloatPoint();
        point.x = rectLeft;
        point.y = rectTop;
        boolean inRectLT = isInRect(pointLT, pointRT, pointRB, pointLB, point);
        //右上角
        point = new FloatPoint();
        point.x = rectRight;
        point.y = rectTop;
        boolean inRectRT = isInRect(pointLT, pointRT, pointRB, pointLB, point);

        //右下角
        point = new FloatPoint();
        point.x = rectRight;
        point.y = rectBottom;
        boolean inRectRB = isInRect(pointLT, pointRT, pointRB, pointLB, point);
        //左下角
        point = new FloatPoint();
        point.x = rectLeft;
        point.y = rectBottom;
        boolean inRectLB = isInRect(pointLT, pointRT, pointRB, pointLB, point);
        return (inRectLT && inRectLB && inRectRT && inRectRB);
    }

    private boolean canTrans(float deltaX, float deltaY, float deltaDegree) {
        //获取4个角的顶点数据，并进行坐标转换
        FloatPoint pointLT = new FloatPoint();
        FloatPoint pointLB = new FloatPoint();
        FloatPoint pointRT = new FloatPoint();
        FloatPoint pointRB = new FloatPoint();

        float halfWidth = mLiveWindow.getWidth() * 1.0F / 2;
        float halfHeight = mLiveWindow.getHeight() * 1.0F / 2;

        FloatPoint centerPoint = new FloatPoint();
        centerPoint.x = mCenterPoint.x - deltaX;
        centerPoint.y = mCenterPoint.y - deltaY;

        pointLT.x = centerPoint.x - halfWidth;
        pointLT.y = centerPoint.y - halfHeight;

        pointLB.x = centerPoint.x - halfWidth;
        pointLB.y = centerPoint.y + halfHeight;

        pointRT.x = centerPoint.x + halfWidth;
        pointRT.y = centerPoint.y - halfHeight;

        pointRB.x = centerPoint.x + halfWidth;
        pointRB.y = centerPoint.y + halfHeight;

        float degree = mTransformData.get(STORYBOARD_KEY_ROTATION_Z) + deltaDegree;
        float scale = mTransformData.get(STORYBOARD_KEY_SCALE_X);
        pointLT = transformData(pointLT, centerPoint, scale, degree);
        pointLB = transformData(pointLB, centerPoint, scale, degree);
        pointRT = transformData(pointRT, centerPoint, scale, degree);
        pointRB = transformData(pointRB, centerPoint, scale, degree);

        //判断四个顶点是否超出LiveWindow坐标范围外
        int[] location = new int[2];
        mCutView.getLocationOnScreen(location);
        int rectLeft = location[0] + mCutView.getDrawRectViewLeft();
        int rectTop = location[1] + mCutView.getDrawRectViewTop();
        int rectRight = rectLeft + mCutView.getRectWidth();
        int rectBottom = rectTop + mCutView.getRectHeight();
        //左上角
        FloatPoint point = new FloatPoint();
        point.x = rectLeft;
        point.y = rectTop;
        boolean inRectLT = isInRect(pointLT, pointRT, pointRB, pointLB, point);
        //右上角
        point = new FloatPoint();
        point.x = rectRight;
        point.y = rectTop;
        boolean inRectRT = isInRect(pointLT, pointRT, pointRB, pointLB, point);

        //右下角
        point = new FloatPoint();
        point.x = rectRight;
        point.y = rectBottom;
        boolean inRectRB = isInRect(pointLT, pointRT, pointRB, pointLB, point);
        //左下角
        point = new FloatPoint();
        point.x = rectLeft;
        point.y = rectBottom;
        boolean inRectLB = isInRect(pointLT, pointRT, pointRB, pointLB, point);
        return (inRectLT && inRectLB && inRectRT && inRectRB);
    }

    //判断点p是否在p1p2p3p4的矩形内
    private boolean isInRect(FloatPoint p1, FloatPoint p2, FloatPoint p3, FloatPoint p4, FloatPoint p) {
        return getCross(p1, p2, p) * getCross(p3, p4, p) >= 0 && getCross(p2, p3, p) * getCross(p4, p1, p) >= 0;
    }

    // 计算 |p1 p2| X |p1 p|
    private float getCross(FloatPoint p1, FloatPoint p2, FloatPoint p) {
        return (p2.x - p1.x) * (p.y - p1.y) - (p.x - p1.x) * (p2.y - p1.y);
    }

    private void connectTimelineWithLiveWindow() {
        if (mStreamingContext == null || mTimeline == null || mLiveWindow == null) {
            return;
        }
        mStreamingContext.connectTimelineWithLiveWindowExt(mTimeline, mLiveWindow);
        mCutView.setDrawRectSize(mLiveWindow.getWidth(), mLiveWindow.getHeight());
    }


    private Rect getClipRect(float deltaX, float deltaY, float deltaDegree) {
        float degree = mTransformData.get(STORYBOARD_KEY_ROTATION_Z) + deltaDegree;
        if (degree == 0) {
            //获取4个角的顶点数据，并进行坐标转换
            FloatPoint pointLT = new FloatPoint();
            FloatPoint pointRB = new FloatPoint();

            float halfWidth = mLiveWindow.getWidth() * 1.0F / 2;
            float halfHeight = mLiveWindow.getHeight() * 1.0F / 2;

            FloatPoint centerPoint = new FloatPoint();
            centerPoint.x = mCenterPoint.x - deltaX;
            centerPoint.y = mCenterPoint.y - deltaY;

            pointLT.x = centerPoint.x - halfWidth;
            pointLT.y = centerPoint.y - halfHeight;

            pointRB.x = centerPoint.x + halfWidth;
            pointRB.y = centerPoint.y + halfHeight;

            float scale = mTransformData.get(STORYBOARD_KEY_SCALE_X);
            pointLT = transformData(pointLT, centerPoint, scale, degree);
            pointRB = transformData(pointRB, centerPoint, scale, degree);

            Rect rect = new Rect();
            rect.left = (int) pointLT.x;
            rect.top = (int) pointLT.y;
            rect.bottom = (int) pointRB.y;
            rect.right = (int) pointRB.x;
            return rect;
        }
        return null;
    }

    private RectF updateRegionData() {
        return getRectEx(mCutView.getRectWidth(), mCutView.getRectHeight(), mLiveWindow.getWidth(), mLiveWindow.getHeight());
    }

    private RectF getRectEx(int rectWidth, int rectHeight, int imageWidth, int imageHeight) {
        imageWidth = (int) (imageWidth * mMinLiveWindowScale);
        imageHeight = (int) (imageHeight * mMinLiveWindowScale);
        RectF rectF = new RectF();
        float imageRatio = imageWidth * 1.0F / imageHeight;
        float rectRatio = rectWidth * 1.0F / rectHeight;
        if (rectRatio > imageRatio) {//宽对齐
            rectF.right = rectWidth * 1.0F / imageWidth;
            rectF.left = -rectF.right;

            float scale = rectWidth * 1.0F / imageWidth;
            float timelineImageHeight = imageHeight * scale;

            rectF.top = rectHeight * 1.0F / timelineImageHeight;
            rectF.bottom = -rectF.top;

        } else {// 高对齐
            rectF.top = rectHeight * 1.0F / imageHeight;
            rectF.bottom = -rectF.top;

            float scale = rectHeight * 1.0F / imageHeight;
            float timelineImageWidth = imageWidth * scale;
            rectF.right = rectWidth * 1.0F / timelineImageWidth;
            rectF.left = -rectF.right;
        }
        return rectF;
    }

    private FloatPoint transformData(FloatPoint point, FloatPoint centerPoint, float scale, float degree, float transX, float transY) {
        float[] src = new float[]{point.x, point.y};
        Matrix matrix = new Matrix();
        matrix.setRotate(degree, centerPoint.x, centerPoint.y);
        matrix.mapPoints(src);
        matrix.setScale(scale, scale, centerPoint.x, centerPoint.y);
        matrix.mapPoints(src);
        matrix.setTranslate(transX, transY);
        matrix.mapPoints(src);
        point.x = src[0];
        point.y = src[1];
        return point;
    }

    private void translateLiveWindow(float deltaX, float deltaY) {
        float newTransX = mTransformData.get(STORYBOARD_KEY_TRANS_X) + deltaX;
        float newTransY = mTransformData.get(STORYBOARD_KEY_TRANS_Y) + deltaY;
        mLiveWindow.setTranslationX(newTransX);
        mLiveWindow.setTranslationY(newTransY);
        mCenterPoint.x += deltaX;
        mCenterPoint.y += deltaY;
        mTransformData.put(STORYBOARD_KEY_TRANS_X, newTransX);
        mTransformData.put(STORYBOARD_KEY_TRANS_Y, newTransY);
    }

    private void scaleLiveWindow(float scaleValue) {
        if (Float.isNaN(scaleValue) || Float.isInfinite(scaleValue)) {
            LogUtils.e("scaleValue is NaN or Infinite!");
            scaleValue = mMinLiveWindowScale;
        }
        mLiveWindow.setScaleX(scaleValue);
        mLiveWindow.setScaleY(scaleValue);
        mTransformData.put(STORYBOARD_KEY_SCALE_X, scaleValue);
        mTransformData.put(STORYBOARD_KEY_SCALE_Y, scaleValue);
    }

    private Map<String, Float> parseToViewTransData(Map<String, Float> originalData) {
        Map<String, Float> result = new HashMap<>();
        float viewScale = originalData.get(STORYBOARD_KEY_SCALE_X) * mMinLiveWindowScale;
        float viewTransX = originalData.get(STORYBOARD_KEY_TRANS_X);
        float viewTransY = originalData.get(STORYBOARD_KEY_TRANS_Y);
        result.put(STORYBOARD_KEY_SCALE_X, viewScale);
        result.put(STORYBOARD_KEY_SCALE_Y, viewScale);
        result.put(STORYBOARD_KEY_TRANS_X, viewTransX);
        result.put(STORYBOARD_KEY_TRANS_Y, viewTransY);
        result.put(STORYBOARD_KEY_ROTATION_Z, originalData.get(STORYBOARD_KEY_ROTATION_Z));
        return result;
    }


    private float getSuitLiveWindowScale(Point rectSize) {
        int liveWindowWidth = mLiveWindow.getWidth();
        int liveWindowHeight = mLiveWindow.getHeight();
        float widthScale = rectSize.x * 1.0F / liveWindowWidth;
        float heightScale = rectSize.y * 1.0F / liveWindowHeight;
        float scale = widthScale;
        if (scale < heightScale) {
            scale = heightScale;
        }
        if (scale < 1) {//只放大，不缩小
            return -1;
        }
        return scale;
    }

    private Point changeCutRectViewNoScale(int ratio) {
        //LiveWindow和 CutRectView需要分别设置，
        // LiveWindow适配CutRectView大小，需要和CutRectView宽和高对齐，
        // 如果LiveWindow宽或高有一边没有对齐，LiveWindow需要进行缩放
        Point size;
        if (mRatioValue > 0) {
            size = getLiveWindowSizeByRatio(mRatioValue);
        } else {
            if (ratio == NvsConstants.AspectRatio.AspectRatio_NoFitRatio) {
                size = getOriginalLiveWindowLayoutParam();
            } else {
                size = getLiveWindowSizeByRatio(ratio);
            }
        }
        mRatioValue = size.x * 1F / size.y;
        setCutRectViewSize(size);
        if (ratio == NvsConstants.AspectRatio.AspectRatio_NoFitRatio) {
            mCutView.setWidthHeightRatio(-1);
        } else {
            mCutView.setWidthHeightRatio(size.x * 1.0F / size.y);
        }
        mRatio = ratio;
        handleCutData(size);
        setCutData(mCutData);
        return size;
    }

    public void setCutData(CutData cutData) {
        if (cutData == null) {
            return;
        }
        mRatio = cutData.getRatio();
        mRatioValue = cutData.getRatioValue();
        Map<String, Float> transformData = cutData.getTransformData();
        Set<String> keySet = transformData.keySet();
        for (String key : keySet) {
            Float aFloat = transformData.get(key);
            if (aFloat != null) {
                mTransformData.put(key, aFloat);
            }
        }
    }

    private void handleCutData(Point size) {
        if ((mCutData != null) && (!mCutData.isOldData())) {//需要考虑老的草稿数据 Old draft data needs to be considered
            Map<String, Float> transFromData = parseTransToView(mOriginalTimelineWidth, mOriginalTimelineHeight, mVideoClip.getFilePath(),
                    new int[]{size.x, size.y}, mCutData.getTransformData());
            mCutData.setTransformData(transFromData);
        }
    }

    /**
     * 转换transform 数据为view范围内的transfrom
     * Convert transform data to transfrom within view scope
     *
     * @param timelineWidth  时间线的宽
     * @param timelineHeight 时间线的高
     * @param filePath       文件路径
     * @param rectSize       裁剪区域的宽高
     * @param transFormData  转换数据
     * @return view坐标系下的转换数据
     */
    private Map<String, Float> parseTransToView(int timelineWidth, int timelineHeight, String filePath,
                                                int[] rectSize, Map<String, Float> transFormData) {
        float transXInTimeline = transFormData.get(StoryboardUtil.STORYBOARD_KEY_TRANS_X);
        float transYInTimeline = transFormData.get(StoryboardUtil.STORYBOARD_KEY_TRANS_Y);
        NvsAVFileInfo avFileInfo = mStreamingContext.getAVFileInfo(filePath);
        int videoStreamRotation = avFileInfo.getVideoStreamRotation(0);
        NvsSize dimension = avFileInfo.getVideoStreamDimension(0);
        int height;
        int width;
        if (videoStreamRotation % 2 == 0) {
            height = dimension.height;
            width = dimension.width;
        } else {
            width = dimension.height;
            height = dimension.width;
        }

        float fileRatio = width * 1F / height;
        float timelineRatio = timelineWidth * 1F / timelineHeight;

        float fileWidthInTimeline;
        float fileHeightInTimeline;
        if (fileRatio > timelineRatio) {//文件宽对齐 File width alignment
            fileWidthInTimeline = timelineWidth;
            fileHeightInTimeline = fileWidthInTimeline / fileRatio;
        } else {//高对齐 High alignment
            fileHeightInTimeline = timelineHeight;
            fileWidthInTimeline = fileHeightInTimeline * fileRatio;
        }
        float rectWidthInTimeline;
        float rectHeightInTimeline;
        float rectRatio = rectSize[0] * 1F / rectSize[1];
        if (rectRatio > fileRatio) {//裁剪区域宽对齐 Cropped area width alignment
            rectWidthInTimeline = fileWidthInTimeline;
            rectHeightInTimeline = rectWidthInTimeline / rectRatio;
        } else {
            rectHeightInTimeline = fileHeightInTimeline;
            rectWidthInTimeline = rectHeightInTimeline * rectRatio;
        }

        float transXInView = transXInTimeline / rectWidthInTimeline * rectSize[0];
        float transYInView = transYInTimeline / rectHeightInTimeline * rectSize[1];
        transFormData.put(StoryboardUtil.STORYBOARD_KEY_TRANS_X, transXInView * fileWidthInTimeline / 2F);
        transFormData.put(StoryboardUtil.STORYBOARD_KEY_TRANS_Y, transYInView * fileHeightInTimeline / 2F);
        return transFormData;
    }

    private Point getLiveWindowSizeByRatio(int ratio) {
        int screenWidth = mPlayerLayout.getWidth();
        int newHeight = mPlayerLayout.getHeight();
        Point size = new Point();

        // 16:9
        if (ratio == NvAsset.AspectRatio_9v16) { //9:16
            size.x = (int) (newHeight * 9.0 / 16);
            size.y = newHeight;
        } else if (ratio == NvAsset.AspectRatio_3v4) { // 3:4
            size.x = screenWidth;
            size.y = (int) (screenWidth * 4.0 / 3);
            if (size.y > newHeight) {
                size.y = newHeight;
                size.x = newHeight * 3 / 4;
            }
        } else if (ratio == NvAsset.AspectRatio_9v18) { //9:16
            size.x = (int) (newHeight * 9.0 / 18);
            size.y = newHeight;
        } else if (ratio == NvAsset.AspectRatio_9v21) { //9:16
            size.x = (int) (newHeight * 9.0 / 21);
            size.y = newHeight;
        } else if (ratio == NvAsset.AspectRatio_1v1) { //1:1
            size.x = screenWidth;
            size.y = screenWidth;
            if (newHeight < screenWidth) {
                size.x = newHeight;
                size.y = newHeight;
            }
        } else if (ratio == NvAsset.AspectRatio_16v9) { // 16:9
            size.x = screenWidth;
            size.y = (int) (screenWidth * 9.0 / 16);
        } else if (ratio == NvAsset.AspectRatio_4v3) { //4:3
            size.x = screenWidth;
            size.y = (int) (screenWidth * 3.0 / 4);
            if (size.y > newHeight) {
                size.x = newHeight * 4 / 3;
                size.y = newHeight;
            }
        } else if (ratio == NvAsset.AspectRatio_18v9) {
            size.x = screenWidth;
            size.y = screenWidth * 9 / 18;
            if (size.y > newHeight) {
                size.x = newHeight * 18 / 9;
                size.y = newHeight;
            }
        } else if (ratio == NvAsset.AspectRatio_21v9) {
            size.x = screenWidth;
            size.y = screenWidth * 9 / 21;
            if (size.y > newHeight) {
                size.x = newHeight * 21 / 9;
                size.y = newHeight;
            }
        } else {
            size.x = screenWidth;
            size.y = newHeight;
        }
        return size;
    }

    private Point getLiveWindowSizeByRatio(float ratio) {
        int screenWidth = mPlayerLayout.getWidth();
        int newHeight = mPlayerLayout.getHeight();
        float viewRatio = screenWidth * 1.0F / newHeight;
        Point size = new Point();
        if (ratio >= viewRatio) { //宽对齐
            size.x = screenWidth;
            size.y = (int) (screenWidth / ratio);
        } else {//高对齐
            size.x = (int) (newHeight * ratio);
            size.y = newHeight;
        }
        return size;
    }


    public void seekTimeline(long timestamp, int seekShowMode) {
        mStreamingContext.seekTimeline(mTimeline, timestamp, NvsStreamingContext.VIDEO_PREVIEW_SIZEMODE_LIVEWINDOW_SIZE, seekShowMode);
    }

    // 获取当前引擎状态
    public int getCurrentEngineState() {
        return mStreamingContext.getStreamingEngineState();
    }

    //停止引擎
    private void stopEngine() {
        if (mStreamingContext != null) {
            mStreamingContext.stop();//停止播放
        }
    }

    private void removeTimeline() {
        TimelineUtil.removeTimeline(mTimeline);
        mTimeline = null;
    }

    private void updateCurPlayTime(long currentTime) {
        mCurrentPlaytimeView.setText(FormatUtils.microsecond2Time(currentTime - mStartTime));
    }

    private void updatePlayProgress(long curPosition) {
        float progress = ((float) (curPosition - mStartTime) / (float) getDuration());
        mSeekBar.setProgress((int) (progress * 100));
        updateCurPlayTime(curPosition);
    }

    private void initTransformData() {
        mTransformData.put(STORYBOARD_KEY_SCALE_X, 1.0F);
        mTransformData.put(STORYBOARD_KEY_SCALE_Y, 1.0F);
        mTransformData.put(STORYBOARD_KEY_ROTATION_Z, 0F);
        mTransformData.put(STORYBOARD_KEY_TRANS_X, 0F);
        mTransformData.put(STORYBOARD_KEY_TRANS_Y, 0F);
    }

    public long getDuration() {
        if (mMaxDuration > 0L) {
            return mMaxDuration;
        }
        if (mTimeline == null) {
            return 0L;
        }
        return mTimeline.getDuration();
    }

    public void playVideo(long startTime, long endTime) {
        // 播放视频
        mStreamingContext.playbackTimeline(mTimeline, startTime, endTime, NvsStreamingContext.VIDEO_PREVIEW_SIZEMODE_LIVEWINDOW_SIZE, true, 0);
    }

}